using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;


namespace Grafica3
{
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class Scene : Microsoft.Xna.Framework.DrawableGameComponent
    {

        private CameraManager camera;

        private Floor floor;

        private SkyBox SkyBox;

        private PointLight[] PointLights;

        private Effect generalEffect, ShadowEffect;

        private Model model, lightPoint;

        private SpriteBatch test;

        public Scene(Game game)
            : base(game)
        {
            camera = new CameraManager(Game);
            Game.Components.Add(camera);
        }

        public override void Initialize()
        {

            base.Initialize();
        }

        protected override void LoadContent()
        {
            GraphicsDevice.RasterizerState = RasterizerState.CullNone;

            test = new SpriteBatch(GraphicsDevice);                        
            BasicEffect abf = new BasicEffect(GraphicsDevice);
            abf.EnableDefaultLighting();

            PointLights = new PointLight[]
            {
                new PointLight(abf.DirectionalLight0),
                new PointLight(abf.DirectionalLight1),
                new PointLight(abf.DirectionalLight2)
            };

            foreach (var l in PointLights)
            {
                l.LoadContent(GraphicsDevice);
            }

            generalEffect = Game.Content.Load<Effect>("Phong");
            ShadowEffect = Game.Content.Load<Effect>("ShadowMap");
            floor = new Floor(GraphicsDevice, Game.Content, generalEffect, PointLights);
            SkyBox = new SkyBox(GraphicsDevice, Game.Content, generalEffect);


            lightPoint = Game.Content.Load<Model>("sun");
            foreach (var m in lightPoint.Meshes)
            {
                foreach (var mp in m.MeshParts){
                    var bf = (BasicEffect)mp.Effect;
                    bf.EnableDefaultLighting();
                    bf.PreferPerPixelLighting = true;
                    bf.EmissiveColor = Color.Yellow.ToVector3() * 0.8f;
                }
            }

            model = Game.Content.Load<Model>("Hebe");
            foreach (var m in model.Meshes)
            {
                foreach (var mp in m.MeshParts)
                {
                    var bf = (BasicEffect)mp.Effect;
                    bf.EnableDefaultLighting();
                    var myfx = generalEffect.Clone();
                    myfx.Parameters["SpecularPower"].SetValue(bf.SpecularPower);
                    myfx.Parameters["SpecularColor"].SetValue(Color.White.ToVector4());
                    myfx.Parameters["DiffuseColor"].SetValue(new Vector4(bf.DiffuseColor, 255));
                    myfx.Parameters["AmbientLightColor"].SetValue(new Vector4(bf.AmbientLightColor, 255));
                    for (int i = 0; i < PointLights.Count(); i++)
                    {
                        myfx.Parameters["PointLight" + i].StructureMembers["Position"].SetValue(new Vector4(PointLights[i].Position, 1));
                        myfx.Parameters["PointLight" + i].StructureMembers["DiffuseColor"].SetValue(PointLights[i].DiffuseColor.ToVector4());
                        myfx.Parameters["PointLight" + i].StructureMembers["SpecularColor"].SetValue(PointLights[i].SpecularColor.ToVector4());
                        myfx.Parameters["PointLight" + i].StructureMembers["Enabled"].SetValue(true);
                    }
                    mp.Effect = myfx;
                }
            }

            base.LoadContent();
        }


        public override void Update(GameTime gameTime)
        {
            if (Keyboard.GetState().IsKeyDown(Keys.P))
            {
                var shadowMaps = (from l in PointLights select l.ShadowMap).ToArray();
                for (int c = 0; c < shadowMaps.Length; c++)
                {
                    var file = System.IO.File.Create("shadow" + c + ".png");
                    shadowMaps[c].SaveAsPng(file, shadowMaps[c].Width, shadowMaps[c].Height);
                }
            }
            base.Update(gameTime);
        }

        public override void Draw(GameTime gameTime)
        {

            UpdateShadows();
            if (Keyboard.GetState().IsKeyDown(Keys.C))
            {
                var oldBlendState = GraphicsDevice.BlendState;
                var oldSamplerState = GraphicsDevice.SamplerStates;
                var oldDepthStencilState = GraphicsDevice.DepthStencilState;
                var oldRasterizerState = GraphicsDevice.RasterizerState;
                test.Begin(0, oldBlendState, SamplerState.PointWrap, oldDepthStencilState, oldRasterizerState);
                test.Draw(PointLights[0].ShadowMap, Vector2.Zero, Color.White);
                test.End();
                GraphicsDevice.BlendState = oldBlendState;
                //GraphicsDevice.SamplerStates = oldSamplerState;
                GraphicsDevice.DepthStencilState = oldDepthStencilState;
                GraphicsDevice.RasterizerState = oldRasterizerState;
            }
            else
            {
                GraphicsDevice.Clear(Color.CornflowerBlue);

                SkyBox.Draw(camera.CurrentCamera);


                floor.Draw(camera.CurrentCamera, PointLights);

                var shadowMaps = (from l in PointLights select l.ShadowMap).ToArray();
                var LVPs = (from l in PointLights select l.CreateLightViewProjectionMatrix(camera.CurrentCamera)).ToArray();

                foreach (var mesh in model.Meshes)
                {
                    foreach (var fx in mesh.Effects)
                    {
                        for (int i = 0; i < shadowMaps.Length; i++)
                        {
                            fx.Parameters["LightVP" + i].SetValue(LVPs[i]);
                            fx.Parameters["ShadowLight" + i].SetValue(shadowMaps[i]);
                        }

                        fx.Parameters["World"].SetValue(Matrix.Identity);
                        //fx.Parameters["View"].SetValue(camera.CurrentCamera.View);
                        //fx.Parameters["Projection"].SetValue(camera.CurrentCamera.Projection);
                        fx.Parameters["EyePosition"].SetValue(camera.CurrentCamera.position);
                        fx.Parameters["View"].SetValue(Matrix.Identity);
                        fx.Parameters["Projection"].SetValue(PointLights[2].CreateLightViewProjectionMatrix(camera.CurrentCamera));
                        fx.Parameters["EyePosition"].SetValue(PointLights[2].Position);
                        fx.Parameters["TextureEnabled"].SetValue(false);
                        fx.Parameters["ShadowEnabled"].SetValue(true);
                        fx.CurrentTechnique = fx.Techniques["PerPixelPhongShading"];

                    }

                    mesh.Draw();
                }
                foreach (var l in PointLights)
                {
                    lightPoint.Draw(Matrix.CreateTranslation(l.Position), camera.CurrentCamera.View, camera.CurrentCamera.Projection);
                }
            }


            base.Draw(gameTime);
        }

        private void UpdateShadows()
        {
            foreach (var light in PointLights)
            {
                GraphicsDevice.SetRenderTarget(light.ShadowMap);
                GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.White, 1.0f, 0);
               // GraphicsDevice.Clear(Color.White);
                foreach (var m in model.Meshes)
                {
                    foreach (var mp in m.MeshParts)
                    {
                        mp.Effect.CurrentTechnique = mp.Effect.Techniques["CreateShadow"];
                        mp.Effect.Parameters["World"].SetValue(Matrix.Identity);
                        mp.Effect.Parameters["View"].SetValue(Matrix.CreateLookAt(light.Position, Vector3.Zero, Vector3.Up));
                        mp.Effect.Parameters["Projection"].SetValue(Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, 1, 110, 150));

                      //  mp.Effect.Parameters["actualLightViewProjection"].SetValue(light.CreateLightViewProjectionMatrix(camera.CurrentCamera));
                    }
                    m.Draw();
                }
            }
            GraphicsDevice.SetRenderTarget(null);

        }
    }
}
